package com.tavio.vaultboy.world;

import org.andengine.extension.tmx.TMXLayer;
import org.andengine.extension.tmx.TMXLoader;
import org.andengine.extension.tmx.TMXTileProperty;
import org.andengine.extension.tmx.TMXTiledMap;
import org.andengine.extension.tmx.util.exception.TMXLoadException;
import org.andengine.opengl.texture.TextureManager;
import org.andengine.opengl.texture.TextureOptions;
import org.andengine.opengl.vbo.VertexBufferObjectManager;

import android.content.res.AssetManager;

import com.tavio.vaultboy.collision.Face;
import com.tavio.vaultboy.connector.Connector;
import com.tavio.vaultboy.element.Button;
import com.tavio.vaultboy.element.Door;
import com.tavio.vaultboy.element.Element;
import com.tavio.vaultboy.element.type.ElementType;
import com.tavio.vaultboy.scene.SceneManager;

public class WorldMap {

	private TMXTiledMap tmxTiledMap;
	private TMXLoader tmxLoader;
	
	private int numRows;
	private int numColumns;
	private int worldWidth;
	private int worldHeight;
	private int tileWidth;
	private int tileHeight;
	private float startPositionX;
	private float startPositionY;
	private WorldTile leftmostTile;
	private WorldTile higherTile;
	
	private WorldTile[][] worldMap;
	
	private Connector[] connectors;
	
	private static WorldMap instance = null;
		
	private WorldMap(){}
	
	public static WorldMap getInstance() {
		if(instance == null)
			instance = new WorldMap();
		return instance;
	}
	
	public void init(String mapFilePath, final AssetManager pAssetManager, final TextureManager pTextureManager, final TextureOptions pTextureOptions, final VertexBufferObjectManager pVertexBufferObjectManager) throws TMXLoadException {
		int rectWidth, rectHeight, iterator;
		
		rectWidth = rectHeight = iterator = 0;
		
		this.tmxLoader = new TMXLoader(pAssetManager, pTextureManager, TextureOptions.BILINEAR_PREMULTIPLYALPHA, pVertexBufferObjectManager);
		this.tmxTiledMap = tmxLoader.loadFromAsset(mapFilePath);
		
		this.numRows = tmxTiledMap.getTileRows();
		this.numColumns = tmxTiledMap.getTileColumns();
		this.tileWidth = tmxTiledMap.getTileWidth();
		this.tileHeight = tmxTiledMap.getTileHeight();
		this.worldHeight = tmxTiledMap.getTMXLayers().get(0).getHeight();
		this.worldWidth = tmxTiledMap.getTMXLayers().get(0).getWidth();
		
		worldMap = new WorldTile[numRows][numColumns];
		
		connectors = new Connector[10];
		
		for(int i = 0; i < connectors.length; i++) {
			connectors[i] = new Connector();
		}
		
		for(int i = 0; i < numRows; i++) {
			for(int j = 0; j < numColumns; j++) {
				worldMap[i][j] = new WorldTile(i,j, tmxTiledMap.getTMXLayers().get(0).getTMXTile(j, i).getTileX(), tmxTiledMap.getTMXLayers().get(0).getTMXTile(j, i).getTileY());
				for(TMXTileProperty prop : tmxTiledMap.getTMXTileProperties(tmxTiledMap.getTMXLayers().get(0).getTMXTile(j, i).getGlobalTileID())) {
					if("colision".equals(prop.getName())) {
						if("true".equals(prop.getValue())) {
							worldMap[i][j].colision = true;
						}
					}
				}
			}
		}
		
		for(int i = 0; i < numRows; i++) {
			for(int j = 0; j < numColumns; j++) {
				for(TMXTileProperty prop : tmxTiledMap.getTMXTileProperties(tmxTiledMap.getTMXLayers().get(1).getTMXTile(j, i).getGlobalTileID())) {
					if("type".equals(prop.getName())) {
						if("startPosition".equals(prop.getValue())) {
							startPositionX = worldMap[i][j].sceneX;
							startPositionY = worldMap[i][j].sceneY;
						} else if("box".equals(prop.getValue())) {
							worldMap[i][j].element = SceneManager.getInstance().addBoxToScene(worldMap[i][j].sceneX, worldMap[i][j].sceneY);
							worldMap[i][j].elementType = ElementType.BOX;
						} else if ("horizontalDoor".equals(prop.getValue())) {
							rectHeight = getTileHeight() / 2;
							rectWidth = getTileWidth();
							leftmostTile = worldMap[i][j];
							iterator = j - 1;
							while(iterator >= 0 && ! worldMap[i][iterator].colision) {
								rectWidth += getTileWidth();
								leftmostTile = worldMap[i][iterator];
								iterator --;
							}
							iterator = j + 1;
							while(iterator < WorldMap.getInstance().numColumns && ! worldMap[i][iterator].colision) {
								rectWidth += getTileWidth();
								iterator ++;
							}
							worldMap[i][j].element = SceneManager.getInstance().addDoorToScene(leftmostTile.sceneX, leftmostTile.sceneY, rectWidth, rectHeight);
							worldMap[i][j].elementType = ElementType.DOOR;
							rectHeight = rectWidth = iterator = 0;
						} else if ("verticalDoor".equals(prop.getValue())) {
							rectHeight = getTileHeight();;
							rectWidth = getTileWidth() / 2;
							higherTile = worldMap[i][j];
							iterator = i - 1;
							while(iterator >= 0 && ! worldMap[iterator][j].colision) {
								rectHeight += getTileHeight();
								higherTile = worldMap[iterator][j];
								iterator --;
							}
							iterator = i + 1;
							while(iterator < WorldMap.getInstance().numRows && ! worldMap[iterator][j].colision) {
								rectHeight += getTileHeight();
								iterator ++;
							}
							worldMap[i][j].element = SceneManager.getInstance().addDoorToScene(higherTile.sceneX, higherTile.sceneY, rectWidth, rectHeight);
							worldMap[i][j].elementType = ElementType.DOOR;
							rectHeight = rectWidth = iterator = 0;
						} else if ("button".equals(prop.getValue())) {
							if(!worldMap[i-1][j].colision) {
								worldMap[i][j].element = SceneManager.getInstance().addButtonToScene(Face.UP,worldMap[i][j].sceneX, worldMap[i][j].sceneY, getTileWidth(), getTileHeight());
								worldMap[i][j].elementType = ElementType.BUTTON;
							}
							else if(!worldMap[i+1][j].colision) { 
								worldMap[i][j].element = SceneManager.getInstance().addButtonToScene(Face.DOWN,worldMap[i][j].sceneX, worldMap[i][j].sceneY, getTileWidth(), getTileHeight());
								worldMap[i][j].elementType = ElementType.BUTTON;
							}
							else if(!worldMap[i][j-1].colision) { 
								worldMap[i][j].element = SceneManager.getInstance().addButtonToScene(Face.LEFT,worldMap[i][j].sceneX, worldMap[i][j].sceneY, getTileWidth(), getTileHeight());
								worldMap[i][j].elementType = ElementType.BUTTON;
							}
							else if(!worldMap[i][j+1].colision) { 
								worldMap[i][j].element = SceneManager.getInstance().addButtonToScene(Face.RIGHT,worldMap[i][j].sceneX, worldMap[i][j].sceneY, getTileWidth(), getTileHeight());
								worldMap[i][j].elementType = ElementType.BUTTON;
							}
						}
					}
				}
			}
		}
		
		/* Now check the 3rd layer to build the connector array */
		
		for(int i = 0; i < numRows; i++) {
			for(int j = 0; j < numColumns; j++) {
				for(TMXTileProperty prop : tmxTiledMap.getTMXTileProperties(tmxTiledMap.getTMXLayers().get(2).getTMXTile(j, i).getGlobalTileID())) {
					if ("connectorId".equals(prop.getName())) {
						if(ElementType.DOOR.equals(worldMap[i][j].elementType)) {
							connectors[Integer.valueOf(prop.getValue())].setDoor((Door)worldMap[i][j].element);
						} else if(ElementType.BUTTON.equals(worldMap[i][j].elementType)) {
							connectors[Integer.valueOf(prop.getValue())].setButton((Button)worldMap[i][j].element);
						}
					}
				}
			}
		}
		
		/* Loop through the connectors and connect the buttons to the doors */
		
		for(int i = 0; i < connectors.length; i++) {
			if(connectors[i].getButton() != null) {
				connectors[i].getButton().connectedDoor = connectors[i].getDoor();
			}
		}
		
		for(int i = 0; i < numRows; i++) {
			for(int j = 0; j < numColumns; j++) {
				if(worldMap[i][j].colision) {
					if(i - 1 >= 0 && ! worldMap[i-1][j].colision && ! worldMap[i][j].northWallSet) {
						rectHeight = 20;
						rectWidth = getTileWidth();
						worldMap[i][j].northWallSet = true;
						iterator = j + 1;
						while(iterator < WorldMap.getInstance().numColumns && worldMap[i][iterator].colision 
								&& ! worldMap[i][iterator].northWallSet && ! worldMap[i-1][iterator].colision) {
								rectWidth += getTileWidth();
								worldMap[i][iterator].northWallSet = true;
								iterator ++;
						}
						final Element rect = new Element(ElementType.WALL,worldMap[i][j].sceneX, worldMap[i][j].sceneY, rectWidth, rectHeight, 0, pVertexBufferObjectManager);
						SceneManager.getInstance().collidableElements.add(rect);
						rectHeight = rectWidth = iterator = 0;
					}
					if(i + 1 < WorldMap.getInstance().numRows && ! worldMap[i+1][j].colision && ! worldMap[i][j].southWallSet) {
						rectHeight = 20;
						rectWidth = getTileWidth();
						worldMap[i][j].southWallSet = true;
						iterator = j + 1;
						while(iterator < WorldMap.getInstance().numColumns && worldMap[i][iterator].colision 
								&& ! worldMap[i+1][iterator].colision && !worldMap[i][iterator].southWallSet) {
								rectWidth += getTileWidth();
								worldMap[i][iterator].southWallSet = true;
								iterator ++;
						}
						final Element rect = new Element(ElementType.WALL, worldMap[i][j].sceneX, worldMap[i][j].sceneY + getTileHeight() - rectHeight, rectWidth, rectHeight, 0, pVertexBufferObjectManager);
						SceneManager.getInstance().collidableElements.add(rect);
						rectHeight = rectWidth = iterator = 0;
					}
					if(j  - 1 >= 0 && ! worldMap[i][j-1].colision && ! worldMap[i][j].westWallSet) {
						rectWidth = 20;
						rectHeight = getTileHeight();
						worldMap[i][j].westWallSet = true;
						iterator = i + 1;
						while(iterator < WorldMap.getInstance().numRows && worldMap[iterator][j].colision 
								&& ! worldMap[iterator][j].westWallSet && ! worldMap[iterator][j-1].colision) {
								rectHeight += getTileHeight();
								worldMap[iterator][j].westWallSet = true;
								iterator ++;
						}
						final Element rect = new Element(ElementType.WALL, worldMap[i][j].sceneX, worldMap[i][j].sceneY, rectWidth, rectHeight, 0, pVertexBufferObjectManager);
						SceneManager.getInstance().collidableElements.add(rect);
						rectHeight = rectWidth = iterator = 0;
					}
					if(j + 1 < WorldMap.getInstance().numColumns && ! worldMap[i][j+1].colision && ! worldMap[i][j].eastWallSet) {
						rectWidth = 20;
						rectHeight = getTileHeight();
						worldMap[i][j].eastWallSet = true;
						iterator = i + 1;
						while(iterator < WorldMap.getInstance().numRows && worldMap[iterator][j].colision 
								&& ! worldMap[iterator][j].eastWallSet && ! worldMap[iterator][j+1].colision) {
								rectHeight += getTileHeight();
								worldMap[iterator][j].eastWallSet = true;
								iterator ++;
						}
						final Element rect = new Element(ElementType.WALL, worldMap[i][j].sceneX + getTileWidth() - rectWidth, worldMap[i][j].sceneY, rectWidth, rectHeight, 0, pVertexBufferObjectManager);
						SceneManager.getInstance().collidableElements.add(rect);
						rectHeight = rectWidth = iterator = 0;
					}
				}
			}
		}
			
		tmxTiledMap.getTMXLayers().get(0).setZIndex(0);
	}
	
	public TMXTiledMap getTmxTiledMap() {
		return tmxTiledMap;
	}
	
	public TMXLayer getTmxLayer(int layerNum) {
		return tmxTiledMap.getTMXLayers().get(layerNum);
	}
	
	public int getNumColumns() {
		return numColumns;
	}
	
	public int getNumRows() {
		return numRows;
	}
	
	public int getTileWidth() {
		return tileWidth;
	}
	
	public int getTileHeight() {
		return tileHeight;
	}

	public int getWorldWidth() {
		return worldWidth;
	}

	public int getWorldHeight() {
		return worldHeight;
	}

	public float getStartPositionX() {
		return startPositionX;
	}

	public float getStartPositionY() {
		return startPositionY;
	}

	
}
